
package creature;

import java.util.ArrayList;

import toxi.geom.Vec2D;
import toxi.physics2d.*;

public class RigidBody
{
	public static final float				HULL_STRENGTH						= 1.5f;
	public static final float				INTEGRITY							= 0.3f;
	public static final float				CENTER_CONNECTION_STRENGTH		= 0.05f;
	public static final float				CONTROL_CONNECTION_STRENGTH	= 0.1f;

	public static final float				NORMAL_OSC_INFLUENCE				= 0.1f;
	public static final float				WALL_OSC_INFLUENCE				= 0.01f;
	public static final float				BOMB_OSC_INFLUENCE				= 0.05f;

	public static final float				PARTICLE_WANDER_SPEED_BASE		= 1.0f;

	private ArrayList<VerletParticle2D>	bodyParticles;
	private ArrayList<VerletSpring2D>	bodySprings;
	private ArrayList<VerletSpring2D>	centerSprings;
	private VerletParticle2D				center;

	private VerletPhysics2D					physics;
	private ShapeTweener						shape;

	private Oscillator						osc									= new Oscillator(2.5f, 0.00001f);	;
	private float								oscInfluence						= NORMAL_OSC_INFLUENCE;

	private float[]							particleWanderAngles;
	private float[]							particleWanderOffsets;
	private float								particleWanderOffsetMin			= 0.1f;
	private float								particleWanderOffsetMax			= 0.5f;
	private float								particlesWanderSpeed				= PARTICLE_WANDER_SPEED_BASE;

	private boolean							oscillate;

	public RigidBody(VerletPhysics2D _physics, boolean _oscillate)
	{
		physics = _physics;
		bodyParticles = new ArrayList<VerletParticle2D>();
		bodySprings = new ArrayList<VerletSpring2D>();
		centerSprings = new ArrayList<VerletSpring2D>();
		shape = new ShapeTweener();
		oscillate = _oscillate;
	}

	public void init(Layout _layout)
	{
		shape.init(_layout.springLengths);

		// particles
		for (int i = 0; i < _layout.particlePositions.length; i++)
		{
			bodyParticles.add(new VerletParticle2D(_layout.particlePositions[i], 0.5f));
		}

		particleWanderAngles = new float[bodyParticles.size()];
		particleWanderOffsets = new float[bodyParticles.size()];
		for (int i = 0; i < particleWanderAngles.length; i++)
		{
			particleWanderAngles[i] = bodyParticles.get(i).sub(_layout.pos).heading();// (float)
			// (Math.random()
			// *
			// 2.0f
			// *
			// Math.PI);
			float wanderOffset = (float) Math.random() * (particleWanderOffsetMax - particleWanderOffsetMin) + particleWanderOffsetMin;
			particleWanderOffsets[i] = wanderOffset;
		}

		// hull springs
		int readIndex = 0;
		VerletParticle2D prev = null;
		for (int i = 0; i < bodyParticles.size(); i++)
		{
			VerletParticle2D p = bodyParticles.get(i);
			if (prev != null)
			{
				if (oscillate)
					bodySprings.add(new OscillatingVerletSpring2D(p, prev, shape.values[readIndex], HULL_STRENGTH, osc, oscInfluence));
				else
					bodySprings.add(new VerletSpring2D(p, prev, shape.values[readIndex], HULL_STRENGTH));

				readIndex++;
			}
			prev = p;
		}

		if (oscillate)
			bodySprings.add(new OscillatingVerletSpring2D(prev, bodyParticles.get(0), shape.values[readIndex], HULL_STRENGTH, osc, oscInfluence));
		else
			bodySprings.add(new VerletSpring2D(prev, bodyParticles.get(0), shape.values[readIndex], HULL_STRENGTH));

		readIndex++;

		// stability springs
		for (int i = 0; i < bodyParticles.size() / 2; i++)
		{
			VerletParticle2D p = bodyParticles.get(i);
			VerletParticle2D o = bodyParticles.get((bodyParticles.size() - 1) - i);

			if (oscillate)
				bodySprings.add(new OscillatingVerletSpring2D(p, o, shape.values[readIndex], INTEGRITY, osc, oscInfluence));
			else
				bodySprings.add(new VerletSpring2D(p, o, shape.values[readIndex], INTEGRITY));

			readIndex++;
		}

		// cross springs
		VerletParticle2D a = bodyParticles.get(0);
		VerletParticle2D b = bodyParticles.get(bodyParticles.size() / 2);
		VerletParticle2D c = bodyParticles.get(bodyParticles.size() / 2 - 1);
		VerletParticle2D d = bodyParticles.get(bodyParticles.size() - 1);

		if (oscillate)
		{
			bodySprings.add(new OscillatingVerletSpring2D(a, b, shape.values[readIndex], INTEGRITY, osc, oscInfluence));
			readIndex++;
			bodySprings.add(new OscillatingVerletSpring2D(c, d, shape.values[readIndex], INTEGRITY, osc, oscInfluence));
			readIndex++;
		}
		else
		{
			bodySprings.add(new VerletSpring2D(a, b, shape.values[readIndex], INTEGRITY));
			readIndex++;
			bodySprings.add(new VerletSpring2D(c, d, shape.values[readIndex], INTEGRITY));
			readIndex++;
		}

		center = new VerletParticle2D(_layout.pos);
		for (int i = 0; i < bodyParticles.size(); i++)
		{
			if (oscillate)
				centerSprings.add(new OscillatingVerletSpring2D(center, bodyParticles.get(i), shape.values[readIndex], CENTER_CONNECTION_STRENGTH, osc, oscInfluence));
			else
				centerSprings.add(new VerletSpring2D(center, bodyParticles.get(i), shape.values[readIndex], CENTER_CONNECTION_STRENGTH));
			readIndex++;
		}

		physics.particles.addAll(bodyParticles);
		physics.addParticle(center);
		physics.springs.addAll(bodySprings);
		physics.springs.addAll(centerSprings);
	}

	public void disableSprings()
	{
		for (int i = 0; i < bodySprings.size(); i++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) bodySprings.get(i);
				s.setStrength(0.0f);
			}
			else
			{
				VerletSpring2D s = (VerletSpring2D) bodySprings.get(i);
				s.setStrength(0.0f);
			}
		}

		for (int i = 0; i < centerSprings.size(); i++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) centerSprings.get(i);
				s.setStrength(0.0f);
			}
			else
			{
				VerletSpring2D s = (VerletSpring2D) centerSprings.get(i);
				s.setStrength(0.0f);
			}
		}
	}

	public void enableSprings()
	{
		for (int i = 0; i < bodySprings.size(); i++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) bodySprings.get(i);
				if (i < bodyParticles.size())
					s.setStrength(HULL_STRENGTH);
				else
					s.setStrength(INTEGRITY);
			}
			else
			{
				VerletSpring2D s = (VerletSpring2D) bodySprings.get(i);
				s.setStrength(0.0f);
				if (i < bodyParticles.size())
					s.setStrength(HULL_STRENGTH);
				else
					s.setStrength(INTEGRITY);
			}
		}

		for (int i = 0; i < centerSprings.size(); i++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) centerSprings.get(i);
				s.setStrength(CENTER_CONNECTION_STRENGTH);
			}
			else
			{
				VerletSpring2D s = (VerletSpring2D) centerSprings.get(i);
				s.setStrength(CENTER_CONNECTION_STRENGTH);
			}
		}
	}

	public void update()
	{
		shape.update();
		osc.update();

		int i = 0;
		for (int j = 0; j < bodySprings.size(); j++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) bodySprings.get(j);
				s.setBaseLength(shape.values()[i]);
			}
			else
			{
				VerletSpring2D s = bodySprings.get(j);
				s.setRestLength(shape.values()[i]);
			}
			i++;
		}

		for (int j = 0; j < centerSprings.size(); j++)
		{
			if (oscillate)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) centerSprings.get(j);
				s.setBaseLength(shape.values()[i]);
			}
			else
			{
				VerletSpring2D s = centerSprings.get(j);
				s.setRestLength(shape.values()[i]);
			}
			i++;
		}
	}
	
	public void rotateWall(float _angle)
	{
		// TODO: find a way to rotate the wall!
	}

	public void wanderParticles()
	{
		particlesWanderSpeed -= particlesWanderSpeed / 20.0f;
		if (particlesWanderSpeed <= 0.001f)
			particlesWanderSpeed = 0.0f;

		for (int i = 0; i < bodyParticles.size(); i++)
		{
			VerletParticle2D p = bodyParticles.get(i);
			float step = (1.0f - 2.0f * (float) Math.random()) * particleWanderOffsets[i];
			particleWanderAngles[i] += step;
			particleWanderAngles[i] %= (float)Math.PI * 2.0f;
			p.x += particlesWanderSpeed * (float) Math.cos(particleWanderAngles[i]);
			p.y += particlesWanderSpeed * (float) Math.sin(particleWanderAngles[i]);
		}
	}

	public void resetWanderProperties()
	{
		particlesWanderSpeed = PARTICLE_WANDER_SPEED_BASE;
		for (int i = 0; i < particleWanderAngles.length; i++)
		{
			particleWanderAngles[i] = bodyParticles.get(i).sub(center).heading();
		}
	}
	
	public float[] getBodyParticleAngles()
	{
		return particleWanderAngles;
	}

	public void set(Layout _layout)
	{
		shape.tweenTo(_layout.springLengths, 5.0f);
	}

	public void setConnector(VerletParticle2D _particle)
	{
		physics.addSpring(new VerletSpring2D(center, _particle, 1.0f, CONTROL_CONNECTION_STRENGTH));
	}

	public VerletParticle2D getCenter()
	{
		return center;
	}

	public Oscillator getOsc()
	{
		return osc;
	}

	public float getOscInfluence()
	{
		return oscInfluence;
	}

	public void setOscInfluence(float _oscInfluence)
	{
		oscInfluence = _oscInfluence;

		if (oscillate)
		{
			for (int i = 0; i < bodySprings.size(); i++)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) bodySprings.get(i);
				s.setOscStrength(oscInfluence);
			}

			for (int i = 0; i < centerSprings.size(); i++)
			{
				OscillatingVerletSpring2D s = (OscillatingVerletSpring2D) centerSprings.get(i);
				s.setOscStrength(oscInfluence);
			}
		}
	}

	public ArrayList<VerletParticle2D> getBodyParticles()
	{
		return bodyParticles;
	}

	public boolean oscillate()
	{
		return oscillate;
	}
}
